﻿using System;
using System.Linq;
using System.Linq.Expressions;

namespace Murry.DataStore.MemoryDB
{
    /// <summary>
    /// Cloning query provider, wraps a normal provider.
    /// </summary>
    internal class CloningQueryProvider : IQueryProvider
    {
        /// <summary>
        /// Create the cloning provider.
        /// </summary>
        /// <param name="queryProvider">Provider to wrap.</param>
        public CloningQueryProvider(IQueryProvider queryProvider)
        {
            QueryProvider = queryProvider;
        }

        /// <summary>
        /// The wrapped QueryProvider.
        /// </summary>
        private IQueryProvider QueryProvider { get; set; }

        /// <summary>
        /// Call wrapped QueryProvider.CreateQuery and then clone the elements.
        /// </summary>
        /// <typeparam name="TElement">Element type.</typeparam>
        /// <param name="expression">Expression.</param>
        /// <returns>Query with cloned elements.</returns>
        public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
        {
            return CreateQuery(expression).Cast<TElement>();
        }

        /// <summary>
        /// Call wrapped QueryProvider.CreateQuery and then clone the elements.
        /// </summary>
        /// <param name="expression">Expression.</param>
        /// <returns>Query with cloned elements.</returns>
        public IQueryable CreateQuery(Expression expression)
        {
            return from o in QueryProvider.CreateQuery(expression).Cast<MemoryPersist>() select o.Clone();
        }

        /// <summary>
        /// Call wrapped QueryProvider.Execute and clone the output.
        /// </summary>
        /// <typeparam name="TElement">Element Type.</typeparam>
        /// <param name="expression">Expression.</param>
        /// <returns>Cloned element.</returns>
        public TElement Execute<TElement>(Expression expression)
        {
            return (TElement)Execute(expression);
        }

        /// <summary>
        /// Call wrapped QueryProvider.Execute and clone the output.
        /// </summary>
        /// <param name="expression">Expression.</param>
        /// <returns>Cloned element.</returns>
        public object Execute(Expression expression)
        {
            return ((ICloneable)QueryProvider.Execute(expression)).Clone();
        }
    }
}
